const WebSocket = require('ws');
const fs = require('fs');

const PORT = process.env.PORT || 8888;
let naluPerChunk = 30,
    interval = 0,
    current = 0,
    start = 0,
    end = 0,
    wss;

let buffer = fs.readFileSync(__dirname + '/fisheye.h264');
// let buffer = fs.readFileSync(__dirname + '/1.h264');
// let buffer = fs.readFileSync(__dirname + '/test_4K.h264');
let fps = 30;
let nalus = extractNALu(buffer);
console.log('nalus len:'+ nalus.length);
let frames = getVideoFrames(nalus);
let total = frames.length;
console.log('frames len:'+ frames.length);
let pframe = 0;


function extractNALu(buffer) {
    let i = 0,
        length = buffer.byteLength,
        value,
        state = 0,
        result = [],
        lastIndex;

    while (i < length) {
        value = buffer[i++];
        // finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01)
        switch (state) {
            case 0:
                if (value === 0) {
                    state = 1;
                }
                break;
            case 1:
                if (value === 0) {
                    state = 2;
                } else {
                    state = 0;
                }
                break;
            case 2:
            case 3:
                if (value === 0) {
                    state = 3;
                } else if (value === 1 && i < length) {
                    if (lastIndex) {
                        // console.log('lastIndex:' + lastIndex + ' i:' + i + ' state:' + state);
                        result.push(buffer.subarray(lastIndex - 4, i - state -1)); //4 - 4, 34 - 3 - 1
                    }
                    lastIndex = i;
                    state = 0;
                } else {
                    state = 0;
                }
                break;
            default:
                break;
        }
    }

    if (lastIndex) {
        result.push(buffer.subarray(lastIndex, length));
    }
    return result;
}

function getVideoFrames(nalus) {
    let nalu,
        units = [],
        samples = [],
        pframecount = 0;

    for (nalu of nalus) {
        // naluObj = new NALU(nalu);
        units.push(nalu);
        ntype = nalu[4] & 0x1f;
        
        // console.log('type:' + ntype);
        if(ntype == 5 || ntype == 1) {
            if(ntype == 5) {
                // console.log('pframecount:' + pframecount);
                // console.log('I frame');
                pframecount = 0;
            } else {
                pframecount++;
            }

            samples.push(units);
            units = [];
        }
        // if (naluObj.type() === NALU.IDR || naluObj.type() === NALU.NDR) {
        //     samples.push({units});
        //     units = [];
        //     if (this.options.clearBuffer) {
        //         if (naluObj.type() === NALU.IDR) {
        //             numberOfFrames.push(this.frameCounter);
        //         }
        //         this.frameCounter++;
        //     }
        // }
    }

    return samples;
}


function openSocket() {
    wss = new WebSocket.Server({ port: PORT });
    console.log('Server ready on port '+PORT);
    wss.on('connection', function connection(ws) {
          console.log('Socket connected. sending data...');
          if (interval) {
              clearInterval(interval);
          }
          ws.on('error', function error(error) {
              console.log('WebSocket error');
          });
          ws.on('close', function close(msg) {
              console.log('WebSocket close');
          });

          interval = setInterval(function() {
            sendChunk();
          }, 1000/fps);
    });
}


function appendByteArray(buffer1, buffer2) {
    let tmp = new Uint8Array((buffer1.byteLength|0) + (buffer2.byteLength|0));
    tmp.set(buffer1, 0);
    tmp.set(buffer2, buffer1.byteLength|0);
    return tmp;
}


function sendChunk() {
    let frame;
    frame = frames[current];
    current++;
    
    if(current == total) current = 0;

    // if(current >= 121) {
    //   console.log('P:' + pframe);
    //   return;
    // }
    
    let data = new Uint8Array();
    let coding = 1; //P
    for(nalu of frame) {
        let ntype = nalu[4] & 0x1f;
        // console.log('type:' + ntype);
        if(ntype == 5) {
            // console.log('P:' + pframe);
            // console.log('I frame');
            pframe = 0;
            coding = 0; //I frame
        } else if(ntype == 1){
            pframe++;
            coding = 1;
        }
        data = appendByteArray(data, nalu);
    }

    let dataview = new DataView(new ArrayBuffer(40));
    let index = 0;
    dataview.setInt32(index, 0xf5010000, true); index += 4;  //sign
    dataview.setInt32(index, 40, true); index += 4;  //hdrLeng
    dataview.setInt32(index, data.length, true); index += 4;  //dataLeng
    dataview.setInt32(index, 0, true); index += 4;  //seq
    dataview.setInt32(index, 0, true); index += 4;  //sec
    dataview.setInt32(index, 0, true); index += 4;  //usec
    dataview.setInt32(index, 0, true); index += 4;  //checkSum
    dataview.setInt16(index, coding, true); index += 2;  //coding
    dataview.setInt16(index, fps, true); index += 2;  //fps
    dataview.setInt16(index, 0, true); index += 2;  //width
    dataview.setInt16(index, 0, true); index += 2;  //height
    dataview.setInt32(index, 0, true); index += 4;  //md
    let header = new Uint8Array(dataview.buffer);

    let package = new Uint8Array();
    package = appendByteArray(package, header);
    package = appendByteArray(package, data);

    // console.log(send.length);
    wss.clients.forEach(function each(client) {
        if (client.readyState === WebSocket.OPEN) {
            try {
                client.send(package);
            } catch(e) {
               console.log(`Sending failed:`, e); 
            }
        }
    });
}



openSocket();
